package com.youdoneed.user.user;

import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.sql.SQLQuery;
import com.querydsl.sql.SQLQueryFactory;
import com.querydsl.sql.dml.SQLUpdateClause;
import com.youdoneed.base.application.AppException;
import com.youdoneed.base.application.ResultMessage;
import com.youdoneed.base.common.GlobalIdWorker;
import com.youdoneed.base.common.JWTUtils;
import com.youdoneed.base.common.RandomString;
import com.youdoneed.base.pagination.Page;
import com.youdoneed.base.pagination.Paging;
import com.youdoneed.common.feign.FileFeignClient;
import com.youdoneed.community.feign.ForumClient;
import com.youdoneed.friends.feign.FriendsClient;
import com.youdoneed.friends.model.Contact;
import com.youdoneed.friends.vo.UserRelationsVO;
import com.youdoneed.user.application.AppConfig;
import com.youdoneed.user.application.PhoneMessage;
import com.youdoneed.user.application.QueryHelper;
import com.youdoneed.user.consts.UserConst;
import com.youdoneed.user.focusregion.FocusRegionService;
import com.youdoneed.user.model.FocusRegion;
import com.youdoneed.user.model.SearchWord;
import com.youdoneed.user.model.User;
import com.youdoneed.user.query.QCoolfaceNotice;
import com.youdoneed.user.query.QFaceSearchRecord;
import com.youdoneed.user.query.QInviteHistory;
import com.youdoneed.user.query.QUser;
import com.youdoneed.user.searchword.SearchWordService;
import com.youdoneed.user.vo.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import sun.rmi.runtime.Log;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Ewing
 * @since 2017-04-21
 **/
@Service
@Transactional(rollbackFor = Throwable.class)
public class UserServiceImpl implements UserService {
    private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    private SQLQueryFactory queryFactory;
    // DAO数据库表映射对象
    private QUser qUser = QUser.User;
    private QInviteHistory qInviteHistory = QInviteHistory.InviteHistory;
    private QFaceSearchRecord qFaceSearchRecord = QFaceSearchRecord.FaceSearchRecord;
    private QCoolfaceNotice qCoolfaceNotice = QCoolfaceNotice.CoolfaceNotice;

    @Autowired
    private SearchWordService searchWordService;
    @Autowired
    private FocusRegionService focusRegionService;

    @Autowired
    private FriendsClient friendsClient;

    @Autowired
    private ForumClient forumClient;

    @Autowired
    private FileFeignClient fileFeignClient;

    @Override
    @Cacheable(value = "User", key = "'User:'+#userId", unless = "#result==null")
    public User getUser(String userId) {
        if (!StringUtils.hasText(userId)) {
            throw new AppException("UserInformationIsEmpty");
        }
        return queryFactory.selectFrom(qUser)
                .where(qUser.userId.eq(userId))
                .fetchOne();
    }

    @Override
    public List<User> getUsers(String[] userIds) {
        if (userIds == null) {
            throw new AppException("UserInformationIsEmpty");
        }
        if (userIds.length == 0) {
            return Collections.emptyList();
        }
        return queryFactory.selectFrom(qUser)
                .where(qUser.userId.in(userIds))
                .fetch();
    }

    @Override
    public User getUserByPhoneNo(String phoneNo) {
        return queryFactory.selectFrom(qUser)
                .where(qUser.phoneNo.eq(phoneNo))
                .fetchFirst();
    }

    @Override
    public List<User> getUserByPhoneNos(String... phoneNos) {
        if (phoneNos == null || phoneNos.length == 0) {
            throw new AppException("PhoneNumberIsEmpty");
        }
        return queryFactory.selectFrom(qUser)
                .where(qUser.phoneNo.in(phoneNos))
                .fetch();
    }

    @Override
    public List<String> findUserIdsByPhoneNos(String[] phoneNos) {
        if (phoneNos == null || phoneNos.length == 0) {
            throw new AppException("PhoneNumberIsEmpty");
        }

        return queryFactory.select(qUser.userId).from(qUser)
                .where(qUser.phoneNo.in(phoneNos)).fetch();
    }

    @Override
    public List<User> getRecommandUsers(String userId) {
        if (!StringUtils.hasText(userId)) {
            throw new AppException("userId为空");
        }
        UserRelationsVO userRelationsVO = friendsClient.findFriendIds(3).getData();
        List<String> friendsIds = userRelationsVO.getFriendIds();
        List<String> blackUserIds = userRelationsVO.getBlackIds();
        friendsIds.addAll(blackUserIds);

        List<User> listUser = queryFactory.selectFrom(qUser)
                .where(qUser.recommendType.eq(1))
                .where(qUser.userId.notIn(friendsIds))
                .where(qUser.imageUrl.isNotNull())
                .orderBy(qUser.recommandTime.desc())
                .fetch();
        return listUser;
    }

    /**
     * 初始化用户缺省信息。
     */
    private void setEmptyToDefault(User user) {
        if (user.getUserId() == null || "".equals(user.getUserId())) {
            user.setUserId(GlobalIdWorker.nextString());
        }

        if (user.getName() == null) {
            if (user.getPhoneNo() == null) {
                user.setName("用户" + RandomString.randomNumberString(4));
            } else {
                int length = user.getPhoneNo().length();
                user.setName("用户" + user.getPhoneNo().substring(length > 4 ? length - 4 : 0));
            }
        }
        if (user.getGender() == null) {
            user.setGender(0);
        }
        if (user.getType() == null) {
            user.setType(1);
        }
        if (user.getTotalArticle() == null) {
            user.setTotalArticle(0);
        }
        if (user.getTotalFriend() == null) {
            user.setTotalFriend(0);
        }
        if (user.getRecommendType() == null) {
            user.setRecommendType(0);
        }
        if (user.getDoNotDisturb() == null) {
            user.setDoNotDisturb(0);
        }
        if (user.getShowNearbyArticle() == null) {
            user.setShowNearbyArticle(1);
        }
        if (user.getAllowMyFriends() == null) {
            user.setAllowMyFriends(1);
        }
        if (user.getAllowMyInfo() == null) {
            user.setAllowMyInfo(1);
        }
        if (user.getAllowMyArticle() == null) {
            user.setAllowMyArticle(1);
        }
        if (user.getAllowMyVideo() == null) {
            user.setAllowMyVideo(1);
        }
        if (user.getAllowAddMe() == null) {
            user.setAllowAddMe(1);
        }
        if (user.getAllowContactForMe() == null) {
            user.setAllowContactForMe(1);
        }
        if (user.getAllowCoolFaceMe() == null) {
            user.setAllowCoolFaceMe(1);
        }
        if (user.getAllowNearbyMe() == null) {
            user.setAllowNearbyMe(1);
        }
        if (user.getAllowRecommendMe() == null) {
            user.setAllowRecommendMe(1);
        }
        if (user.getAutoAgreeAddMe() == null) {
            user.setAutoAgreeAddMe(0);
        }
        if (user.getCreateTime() == null) {
            user.setCreateTime(new Date());
        }
    }

    /**
     * 注册到环信。
     */
    private void registerHuanXin(User user) {
        // 注册到环信
        String userId = user.getUserId();
        ResultMessage resultMessage = friendsClient
                .singleRegister(userId, DigestUtils.md5DigestAsHex(userId.getBytes()));
        LOGGER.info("注册用户到环信，返回结果："+resultMessage.getMessage() + resultMessage.getData());
        if (!resultMessage.isSuccess()) {
            throw new AppException(resultMessage.getMessage());
        }
    }

    /**
     * 手机号登陆或注册
     */
    @Override
    public ResultMessage<User> phoneNoLoginOrRegister(String phoneNo, String nation, String imgUrl) {
        // 校验参数
        if (phoneNo == null || !AppConfig.PHONE_NO_PATTERN.matcher(phoneNo).matches()) {
            throw new AppException("PhoneNumberFormatIncorrect");
        }

        // 手机号防重复
        User user = getUserByPhoneNo(phoneNo);
        if (user != null) {
            return new ResultMessage<>(user).setCode(1);
        }

        // 初始化用户缺省信息
        user = new User();
        user.setNation(nation);
        user.setPhoneNo(phoneNo);
        setEmptyToDefault(user);

        // 保存用户
        queryFactory.insert(qUser)
                .populate(user)
                .execute();

        // 注册到环信
        registerHuanXin(user);

        //查看谁coolface我，并保存到通知中
        /*LOGGER.info("查看谁找过我!");
        String token = JWTUtils.generateToken(UserConst.USER_ID_KEY, user.getUserId());
        token = JWTUtils.putClaimsData(token, "exp", System.currentTimeMillis() / 1000 + 3600);

        AppSearchResult appSearchResult = friendsClient.whoCoolfaceMe(token).getData();
        if (null != appSearchResult) {
            LOGGER.info("获取谁找过我数据成功!");
            List<FaceSearchUser> listFaceSearchUser = appSearchResult.getUsers();
            if ((null != listFaceSearchUser)
                    && (0 < listFaceSearchUser.size())) {
                LOGGER.info("以前有人找过我，保存搜索的记录!");
                for (FaceSearchUser faceSearchUser : listFaceSearchUser) {
                    FaceSearchRecord record = new FaceSearchRecord();
                    record.setBornDate(faceSearchUser.getBornDate());
                    record.setConfirmId(faceSearchUser.getConfirmId());
                    record.setCreateDate(new Date());
                    record.setFacePicUrl(faceSearchUser.getFacePicUrl());
                    record.setFriendHandle(faceSearchUser.getFriendHandle());
                    record.setGender(faceSearchUser.getGender());
                    record.setImageUrl(faceSearchUser.getImageUrl());
                    record.setJobName(faceSearchUser.getJobName());
                    record.setMsg(faceSearchUser.getMsg());
                    record.setName(faceSearchUser.getName());
                    record.setSimilarRate(faceSearchUser.getSimilarRate());
                    String searchId = GlobalIdWorker.nextString();
                    record.setSearchId(searchId);
                    record.setUserId(faceSearchUser.getUserId());
                    queryFactory.insert(qFaceSearchRecord)
                            .populate(record)
                            .execute();
                    LOGGER.info("保存coolface我的记录成功");

                    //保存对当前用户的通知
                    CoolfaceNotice notice1 = new CoolfaceNotice();
                    notice1.setCreateDate(new Date());
                    notice1.setNoticeId(GlobalIdWorker.nextString());
                    notice1.setHasRead(0);
                    notice1.setNoticeType(PrivacyConst.coolfaceRecommand);
                    notice1.setNoticeUserId(user.getUserId());
                    notice1.setUserId(record.getUserId());
                    notice1.setSearchId(searchId);
                    queryFactory.insert(qCoolfaceNotice)
                            .populate(notice1)
                            .execute();
                    LOGGER.info("保存通知当前用户我曾经被人coolface成功");

                    //保存给他人的通知（通知别人你以前coolface的用户注册了）
                    CoolfaceNotice notice2 = new CoolfaceNotice();
                    notice2.setCreateDate(new Date());
                    notice2.setNoticeId(GlobalIdWorker.nextString());
                    notice2.setHasRead(0);
                    notice2.setNoticeType(PrivacyConst.systemRecommand);
                    notice2.setNoticeUserId(record.getUserId());
                    notice2.setUserId(user.getUserId());
                    notice2.setSearchId(searchId);
                    queryFactory.insert(qCoolfaceNotice)
                            .populate(notice2)
                            .execute();
                    LOGGER.info("保存通知其他用户你以前coolface过的用户注册了成功");
                }
            }
        }*/

        return new ResultMessage<>(user).setCode(2);
    }

    /**
     * 手机号登陆或注册，不会直接保存到数据库  code ：1 平台老用户    3：临时用户
     */
    @Override
    public ResultMessage<User> phoneNoLoginOrRegisterNotSave(String phoneNo, String nation) {
        // 校验参数
        if (phoneNo == null || !AppConfig.PHONE_NO_PATTERN.matcher(phoneNo).matches()) {
            throw new AppException("PhoneNumberFormatIncorrect");
        }

        // 手机号防重复
        User user = getUserByPhoneNo(phoneNo);
        if (user != null) {
            return new ResultMessage<>(user).setCode(1);
        }

        return new ResultMessage<>(user).setCode(3);
    }

    /**
     * 根据条件查询用户。
     */
    @Override
    public Page<User> findUsers(Paging paging, String name, String phoneNo) {
        SQLQuery<User> query = queryFactory.selectFrom(qUser);
        // 用户名称
        if (StringUtils.hasText(name)) {
            query.where(qUser.name.contains(name));
        }
        // 电话号码
        if (StringUtils.hasText(phoneNo)) {
            query.where(qUser.phoneNo.eq(phoneNo));
        }
        return QueryHelper.queryPage(paging, query);
    }

    /**
     * 用户修改自己的基本信息。
     *
     * @param userParam
     */
    @Override
    @CacheEvict(value = "User", key = "'User:'+#userParam.userId", condition = "#userParam!=null")
    public long setUserBasicInfo(UserBasicInfo userParam, String token) {
        LOGGER.info("修改用户信息；token："+token);
        if (userParam == null || !StringUtils.hasText(userParam.getUserId())) {
            throw new AppException("UserInformationIsEmpty");
        }
        // 创建一个新对象进行选择性更新
        User updateUser = new User();
        // 用户基本信息更新
        if (StringUtils.hasText(userParam.getName())) {
            if (userParam.getName().length() > 10) {
                throw new AppException("User name too long.");
            }

            if(userParam.getName().indexOf(" ")!= -1){
                throw new AppException("User name is illegal.");
            }
            // 更新帖子用户名称
            forumClient.updateUserInfo(userParam.getUserId(), userParam.getName(), null,null);
            // 更新环信用户昵称
            friendsClient.updateNickName(userParam.getUserId(), userParam.getName());
            updateUser.setName(userParam.getName());
        }
        if (StringUtils.hasText(userParam.getJobName())) {
            // 更新帖子用户名称
            forumClient.updateUserInfo(userParam.getUserId(), null, null,userParam.getJobName());
            updateUser.setJobName(userParam.getJobName());
        }
        if (StringUtils.hasText(userParam.getOrganization())) {
            updateUser.setOrganization(userParam.getOrganization());
        }
        if (userParam.getBornDate() != null) {
            updateUser.setBornDate(userParam.getBornDate());
        }
        if (userParam.getGender() != null) {
            updateUser.setGender(userParam.getGender());
        }

        if (null != userParam.getRecommendType()) {
            updateUser.setRecommendType(userParam.getRecommendType());
            updateUser.setRecommandTime(new Date());
        }

        // 用户位置信息更新
        if (StringUtils.hasText(userParam.getProvince())) {
            updateUser.setProvince(userParam.getProvince());
        }
        if (StringUtils.hasText(userParam.getCity())) {
            updateUser.setCity(userParam.getCity());
        }
        if (StringUtils.hasText(userParam.getRegion())) {
            updateUser.setRegion(userParam.getRegion());
        }
        if (StringUtils.hasText(userParam.getAddress())) {
            updateUser.setAddress(userParam.getAddress());
        }
        if (userParam.getLongitude() != null) {
            updateUser.setLongitude(new BigDecimal(userParam.getLongitude())
                    .setScale(6, BigDecimal.ROUND_HALF_UP).doubleValue());
        }
        if (userParam.getLatitude() != null) {
            updateUser.setLatitude(new BigDecimal(userParam.getLatitude())
                    .setScale(6, BigDecimal.ROUND_HALF_UP).doubleValue());
        }
        // 更新消息通知
        if (userParam.getDoNotDisturb() != null) {
            updateUser.setDoNotDisturb(userParam.getDoNotDisturb());
        }
        if (userParam.getNoticeRegId() != null) {
            updateUser.setNoticeRegId(userParam.getNoticeRegId());
        }

        // 用户头像更新
        if (StringUtils.hasText(userParam.getImageUrl())) {
            forumClient.updateUserInfo(userParam.getUserId(),
                    null, userParam.getImageUrl(),null);
            // 重新提取人脸特征
            if (userParam.getImageHasFace() != null
                    && userParam.getImageHasFace()) {
                try {
                    if(!StringUtils.hasText(token)){
                        token = JWTUtils.generateToken(UserConst.USER_ID_KEY, userParam.getUserId());
                        LOGGER.info("token:"+token);
                        token = JWTUtils.putClaimsData(token, "exp", System.currentTimeMillis() / 1000 + 3600);
                    }
                    LOGGER.info("修改特征值!token："+token);
                    friendsClient.genImgFeature(userParam.getImageUrl(), token);
                } catch (Exception e) {
                    LOGGER.error("从照片：" + userParam.getImageUrl()
                            + " 中提取人脸特征失败：" + e.getMessage());
                }
            }
            updateUser.setImageUrl(userParam.getImageUrl());
        }
        // 用户背景图更新
        if (StringUtils.hasText(userParam.getBackgroundUrl())) {
            String oldBackgroundUrl = queryFactory
                    .select(qUser.backgroundUrl).from(qUser)
                    .where(qUser.userId.eq(userParam.getUserId()))
                    .fetchFirst();
            if (StringUtils.hasText(oldBackgroundUrl)
                    && !oldBackgroundUrl.equals(userParam.getBackgroundUrl())) {
                fileFeignClient.deleteByUrl(oldBackgroundUrl);
            }
            updateUser.setBackgroundUrl(userParam.getBackgroundUrl());
        }
        // 用户视频头像更新
        if (userParam.getVideoUrl() != null) {
            String oldVideoUrl = queryFactory
                    .select(qUser.videoUrl).from(qUser)
                    .where(qUser.userId.eq(userParam.getUserId()))
                    .fetchFirst();
            if (StringUtils.hasText(oldVideoUrl)
                    && !oldVideoUrl.equals(userParam.getVideoUrl())) {
                fileFeignClient.deleteByUrl(oldVideoUrl);
            }
            // 如果是空串，设置为null
            updateUser.setVideoUrl(userParam.getVideoUrl()
                    .length() == 0 ? null : userParam.getVideoUrl());
        }

        if (StringUtils.hasText(userParam.getVideoUrl())) {
            String oldVideoUrl = queryFactory
                    .select(qUser.videoUrl).from(qUser)
                    .where(qUser.userId.eq(userParam.getUserId()))
                    .fetchFirst();
            if (StringUtils.hasText(oldVideoUrl)
                    && !oldVideoUrl.equals(userParam.getVideoUrl())) {
                fileFeignClient.deleteByUrl(oldVideoUrl);
            }
            updateUser.setVideoUrl(userParam.getVideoUrl());
        }

        // 只更新有值的属性
        SQLUpdateClause update = queryFactory.update(qUser)
                .populate(updateUser)
                .where(qUser.userId.eq(userParam.getUserId()));
        return update.isEmpty() ? 0L : update.execute();
    }

    /**
     * 用户修改自己的基本信息。
     *
     * @param user
     */
    @Override
    public ResultMessage<User> saveUser(User user) {
        if (user == null ) {
            throw new AppException("UserInformationIsEmpty");
        }

        setEmptyToDefault(user);




        //        // 保存用户
        long result = queryFactory.insert(qUser)
                .populate(user)
                .execute();



        if (result==0){
            LOGGER.error("用户信息插入数据库失败");
            throw new AppException("保存用户信息到数据库失败");
        }

        //         注册到环信
        registerHuanXin(user);

        // 重新提取人脸特征
        String token = JWTUtils.generateToken(UserConst.USER_ID_KEY, user.getUserId());
        try {
            LOGGER.info("修改特征值!token："+token);
            friendsClient.genImgFeature(user.getImageUrl(), token);
        } catch (Exception e) {
            LOGGER.error("从照片：" + user.getImageUrl()
                    + " 中提取人脸特征失败：" + e.getMessage());
        }


        return new ResultMessage<User>().setMessage(token).setData(user);
    }

    // 纬度每米
    public static final double LATITUDE_PER_METER = 0.000008998200359928014;
    // 弧度每角度
    public static final double ARC_PER_ANGLE = 0.017453292519943295;

    /**
     * 附近的用户。
     */
    @Override
    public List<UserShow> nearbyUsers(Paging paging, String userId, NearbyUserParam param) {
        if (!StringUtils.hasText(userId)) {
            throw new AppException("UserInformationIsEmpty");
        }
        if (param.getLongitude() == null || param.getLatitude() == null) {
            //如果不传经纬度则返回空
            return null;
        }
        if (param.getMaxDistance() == null) {
            param.setMaxDistance(60000);
        }
        // 该接口不需要统计总数
        paging.setCount(false);

        // 如果有关注区域，则显示关注区域周围的用户
        FocusRegion focusRegion = focusRegionService.checkedFocusRegion(userId);
        if (focusRegion != null) {
            param.setLatitude(focusRegion.getLatitude());
            param.setLongitude(focusRegion.getLongitude());
        }

        // 计算出纬度距离
        double maxLatitudeDistance = param.getMaxDistance() * LATITUDE_PER_METER;
        // 距离的平方
        double maxDistanceSquare = maxLatitudeDistance * maxLatitudeDistance;

        // 查询附近的用户
        SQLQuery<UserShow> query = queryFactory.select(QueryHelper
                .matchToBean(UserShow.class, qUser))
                .where(qUser.imageUrl.isNotEmpty())
                .from(qUser);

        // 纬度距离=cos(纬度转换为弧度)*经度距离
        double ratio = Math.cos(param.getLatitude() * ARC_PER_ANGLE);
        NumberExpression<Double> distanceSquare = qUser.longitude.add(-param.getLongitude())
                .multiply(qUser.longitude.add(-param.getLongitude()))
                .multiply(ratio * ratio) // 经度转换成等距的纬度
                .add(qUser.latitude.add(-param.getLatitude())
                        .multiply(qUser.latitude.add(-param.getLatitude())));
        // 限制距离范围
        query.where(qUser.longitude.isNotNull().and(qUser.latitude.isNotNull()));
        query.where(distanceSquare.lt(maxDistanceSquare));

        // 必须允许附近的人
        query.where(qUser.allowNearbyMe.eq(1));


        // 获取用户的关系
        UserRelationsVO relations = friendsClient.findFriendIds(4).getData();
        // 排除自己和黑名单
        List<String> userIdNotIn = new ArrayList<>();
        userIdNotIn.add(userId);
        userIdNotIn.addAll(relations.getBlackIds());
        // 如果是为了加好友，必须允许加好友，并且好友和存在好友请求的要排除
        if (param.getForAddFriend() != null && param.getForAddFriend()) {
            query.where(qUser.allowAddMe.eq(1));
            userIdNotIn.addAll(relations.getFriendIds());
            userIdNotIn.addAll(relations.getHasConfirmUserIds());
        }
        query.where(qUser.userId.notIn(userIdNotIn));

        // 如果传了性别，添加性别限制
        if (param.getGender() != null) {
            query.where(qUser.gender.eq(param.getGender()));
        }

        // 根据距离的平方升序排序
        query.orderBy(distanceSquare.asc());

        LOGGER.info("查附近的人的参数：" + query.getSQL().getBindings());
        List<UserShow> users = QueryHelper.queryPage(paging, query).getContent();


        users.forEach(user -> {
            // 处理好友关系
            setFriendHandle(relations, user);
            // 计算到的用户的距离（米），以纬度为标准，经度换算回等效纬度距离
            double longitudeDistance = (user.getLongitude() - param.getLongitude()) * ratio;
            double latitudeDistance = user.getLatitude() - param.getLatitude();
            double userDistances = longitudeDistance * longitudeDistance
                    + latitudeDistance * latitudeDistance;
            user.setDistance((int) Math.round(Math.sqrt(userDistances) * 111133.33333333));
        });
        return users;
    }

    /**
     * 处理好友关系。
     */
    private void setFriendHandle(UserRelationsVO relations, UserShow user) {
        if (relations.getFriendIds().contains(user.getUserId())) {
            user.setFriendHandle(1); // 是好友
        } else if (relations.getHasConfirmUserIds().contains(user.getUserId())) {
            user.setFriendHandle(3); // 发了加好友请求
        } else {
            user.setFriendHandle(2); // 非好友
        }
    }

    /**
     * 获取用户展示信息。
     */
    @Override
    public UserShow getUserShow(String userId) {
        if (!StringUtils.hasText(userId)) {
            throw new AppException("UserInformationIsEmpty");
        }
        // 获取多少人关注了我
        ResultMessage<Integer> attentionMeResult = forumClient.getTotalAttentionMe(userId);
        if (!attentionMeResult.isSuccess()) {
            throw new AppException(attentionMeResult.getMessage());
        }
        // 获取多少人给我点赞
        ResultMessage<Integer> agreeMeResult = forumClient.getTotalAgreeMe(userId);
        if (!agreeMeResult.isSuccess()) {
            throw new AppException(agreeMeResult.getMessage());
        }
        // 获取用户基本信息
        UserShow userShow = queryFactory.select(
                QueryHelper.matchToBean(UserShow.class, qUser))
                .from(qUser)
                .where(qUser.userId.eq(userId))
                .fetchOne();
        userShow.setAttentionMe(attentionMeResult.getData());
        userShow.setAgreeMe(agreeMeResult.getData());
        return userShow;
    }

    @Override
    public List<UserBasicInfo> findInUserIds(String[] userIds, String name) {
        if (userIds == null || userIds.length == 0) {
            throw new AppException("UserInformationIsEmpty");
        }
        return queryFactory.select(
                QueryHelper.matchToBean(UserBasicInfo.class, qUser))
                .from(qUser)
                .where(qUser.userId.in(userIds))
                .where(qUser.name.contains(name))
                .fetch();
    }

    /**
     * 短信邀请好友注册。
     */
    @Override
    public void inviteUser(String myUserId, String otherPhoneNo) {
        if (!StringUtils.hasText(myUserId)) {
            throw new AppException("UserInformationIsEmpty");
        }
        if (otherPhoneNo == null || !AppConfig.PHONE_NO_PATTERN.matcher(otherPhoneNo).matches()) {
            throw new AppException("PhoneNumberFormatIncorrect");
        }

        // 查询用户所在的国家
        String nation = queryFactory.select(qUser.nation)
                .from(qUser)
                .where(qUser.userId.eq(myUserId))
                .fetchOne();
        if (nation == null) {
            throw new AppException("UserInformationIsEmpty");
        }

        // 发送短信邀请
        if ("86".equals(nation)) {
            PhoneMessage.sendCheckCode(otherPhoneNo, "0000");
        } else {
            PhoneMessage.sendNationalCode(otherPhoneNo, "0000");
        }

        // 更新邀请历史记录
        if (queryFactory.update(qInviteHistory)
                .set(qInviteHistory.inviteTime, new Date())
                .where(qInviteHistory.userId.eq(myUserId))
                .where(qInviteHistory.phoneNo.eq(otherPhoneNo))
                .execute() < 1) {
            // 没有记录被更新，新增邀请历史记录
            queryFactory.insert(qInviteHistory)
                    .set(qInviteHistory.userId, myUserId)
                    .set(qInviteHistory.phoneNo, otherPhoneNo)
                    .set(qInviteHistory.inviteTime, new Date())
                    .execute();
        }
    }

    /**
     * 判断用户的通讯录的号码今天是否已邀请。
     */
    @Override
    public List<String> hasInvitedToday(String myUserId, String[] otherPhoneNos) {
        if (!StringUtils.hasText(myUserId)) {
            throw new AppException("UserInformationIsEmpty");
        }

        List<String> temp = new ArrayList<>();
        for (String otherPhoneNo : otherPhoneNos) {
            if (otherPhoneNo == null || !AppConfig.PHONE_NO_PATTERN.matcher(otherPhoneNo).matches()) {
                continue;
            }
            temp.add(otherPhoneNo);
        }

        String[] phones = new String[temp.size()];
        temp.toArray(phones);

        //今天零点零分零秒的毫秒数
        long zero = System.currentTimeMillis() / 86400000 * 86400000
                - TimeZone.getDefault().getRawOffset();
        // 查询用户通讯录中今天已邀请的号码
        return queryFactory.select(qInviteHistory.phoneNo)
                .from(qInviteHistory)
                .where(qInviteHistory.userId.eq(myUserId))
                .where(qInviteHistory.phoneNo.in(otherPhoneNos))
                .where(qInviteHistory.inviteTime.after(new Date(zero)))
                .fetch();
    }

    @Override
    public long changeStatistics(String userId, Integer totalArticleChange, Integer totalFriendChange) {
        if (!StringUtils.hasText(userId)) {
            throw new AppException("UserInformationIsEmpty");
        }
        SQLUpdateClause update = queryFactory.update(qUser)
                .where(qUser.userId.eq(userId));
        if (totalArticleChange != null) {
            update.set(qUser.totalArticle, qUser.totalArticle.add(totalArticleChange));
        }
        if (totalFriendChange != null) {
            update.set(qUser.totalFriend, qUser.totalFriend.add(totalFriendChange));
        }
        return update.isEmpty() ? 0 : update.execute();
    }

    @Override
    public Page<User> adminFindUsers(Paging paging, FindUserParam findUserParam) {
        SQLQuery<User> query = queryFactory.selectFrom(qUser);
        // 名称
        if (StringUtils.hasText(findUserParam.getName())) {
            query.where(qUser.name.contains(findUserParam.getName()));
        }
        // 电话号码
        if (StringUtils.hasText(findUserParam.getPhoneNo())) {
            query.where(qUser.phoneNo.eq(findUserParam.getPhoneNo()));
        }
        // 类型
        if (findUserParam.getType() != null) {
            query.where(qUser.type.eq(findUserParam.getType()));
        }
        // 性别
        if (findUserParam.getGender() != null) {
            query.where(qUser.gender.eq(findUserParam.getGender()));
        }
        // 省份
        if (StringUtils.hasText(findUserParam.getProvince())) {
            query.where(qUser.province.eq(findUserParam.getProvince()));
        }
        // 城市
        if (StringUtils.hasText(findUserParam.getCity())) {
            query.where(qUser.city.eq(findUserParam.getCity()));
        }
        // 用户被推荐类型
        if (findUserParam.getRecommendType() != null) {
            query.where(qUser.recommendType.eq(findUserParam.getRecommendType()));
        }
        // 文章数范围
        if (findUserParam.getMinArticles() != null) {
            query.where(qUser.totalArticle.goe(findUserParam.getMinArticles()));
        }
        if (findUserParam.getMaxArticles() != null) {
            query.where(qUser.totalArticle.loe(findUserParam.getMaxArticles()));
        }
        // 好友数范围
        if (findUserParam.getMinFriends() != null) {
            query.where(qUser.totalFriend.goe(findUserParam.getMinFriends()));
        }
        if (findUserParam.getMaxFriends() != null) {
            query.where(qUser.totalFriend.loe(findUserParam.getMaxFriends()));
        }
        // 注册时间范围
        if (findUserParam.getStartTime() != null) {
            query.where(qUser.createTime.after(findUserParam.getStartTime()));
        }
        if (findUserParam.getEndTime() != null) {
            query.where(qUser.createTime.before(findUserParam.getEndTime()));
        }
        return QueryHelper.queryPage(paging, query);
    }

    @Override
    public User adminAddUser(User user) {
        if (user == null || !StringUtils.hasText(user.getName())) {
            throw new AppException("UserInformationIsEmpty");
        }
        // 用户名防重复
        if (queryFactory.selectFrom(qUser)
                .where(qUser.name.eq(user.getName()))
                .fetchFirst() != null) {
            throw new AppException("UserNameAlreadyExists");
        }
        // 手机号防重复
        if (StringUtils.hasText(user.getPhoneNo())
                && queryFactory.selectFrom(qUser)
                .where(qUser.phoneNo.eq(user.getPhoneNo()))
                .fetchFirst() != null) {
            throw new AppException("UserPhoneNoAlreadyExists");
        }

        // 密码加密检查
        if (StringUtils.hasText(user.getPassword()) && user.getPassword().length() < 32) {
            throw new AppException("密码必须加密！");
        }

        // 初始化用户缺省信息
        setEmptyToDefault(user);

        //提取特征值
        if ((StringUtils.hasText(user.getImageUrl()))
                && (StringUtils.hasText(user.getUserId()))) {
            try {
                String token = JWTUtils.generateToken(UserConst.USER_ID_KEY, user.getUserId());
                token = JWTUtils.putClaimsData(token, "exp", System.currentTimeMillis() / 1000 + 3600);
                friendsClient.genImgFeature(user.getImageUrl(), token);
            } catch (Exception e) {
                LOGGER.error("从照片：" + user.getImageUrl()
                        + " 中提取人脸特征失败：" + e.getMessage());
            }
        }

        // 保存用户
        queryFactory.insert(qUser).populate(user).execute();

        registerHuanXin(user);
        return user;
    }

    @Override
    public UserStatistics userStatistics() {
        //今天零点零分零秒的毫秒数
        long zero = System.currentTimeMillis() / 86400000 * 86400000
                - TimeZone.getDefault().getRawOffset();
        Date todayStart = new Date(zero);
        return queryFactory.select(Projections.bean(UserStatistics.class,
                Expressions.asNumber(1).count().as("totalUser"),
                qUser.createTime.after(todayStart)
                        .castToNum(Long.class).sum().as("newUserToday")))
                .from(qUser)
                .fetchOne();
    }

    @Override
    public Long getUserCount() {
        Long count = queryFactory.selectFrom(qUser).fetchCount();
        return count;
    }

    @Override
    public List<UserShow> findOthers(String userId, Paging paging, String name) {
        if (!StringUtils.hasText(userId)) {
            throw new AppException("UserInformationIsEmpty");
        }
        if (!StringUtils.hasText(name)) {
            return Collections.emptyList();
        }
        // 该接口不需要统计总数
        paging.setCount(false);

        // 新增搜索词
        SearchWord searchWord = new SearchWord();
        searchWord.setUserId(userId);
        searchWord.setWord(name);
        searchWordService.addSearchWord(searchWord);

        // 根据名称模糊搜索、或根据手机号精确搜索用户
        UserRelationsVO relations = friendsClient.findFriendIds(4).getData();
        SQLQuery<UserShow> query = queryFactory
                .select(QueryHelper.matchToBean(UserShow.class, qUser))
                .from(qUser)
                .where(qUser.name.contains(name).or(qUser.phoneNo.eq(name)))
                .where(qUser.userId.notIn(relations.getBlackIds()));
        List<UserShow> userShows = QueryHelper.queryPage(paging, query).getContent();

        // 判断用户ID集合是否为好友
        String[] userIds = new String[userShows.size()];
        for (int i = 0; i < userIds.length; i++) {
            userIds[i] = userShows.get(i).getUserId();
        }
        userShows.forEach((user) -> setFriendHandle(relations, user));
        return userShows;
    }

    /**
     * 获取用户通讯录中已注册的用户。
     */
    @Override
    public List<ContactUser> getContactUsers(String userId) {
        if (!StringUtils.hasText(userId)) {
            throw new AppException("UserInformationIsEmpty");
        }
        List<ContactUser> contactUsers = new ArrayList<>();
        List<Contact> contacts = friendsClient.getContacts().getData();
        // 根据电话号码查询已注册用户
        List<String> phoneNos = contacts.stream().map(Contact::getPhoneNo)
                .collect(Collectors.toList());
        List<User> users = queryFactory.selectFrom(qUser)
                .where(qUser.phoneNo.in(phoneNos))
                .fetch();
        for (User user : users) {
            for (Contact contact : contacts) {
                if (contact.getPhoneNo().equals(user.getPhoneNo())) {
                    ContactUser contactUser = new ContactUser();
                    contactUser.setUser(user);
                    contactUser.setContactName(contact.getName());
                    contactUser.setContactPhoneNo(contact.getPhoneNo());
                    contactUsers.add(contactUser);
                    break; // 该结束内层循环了
                }
            }
        }
        return contactUsers;
    }


    /**
     * 临时提供给 为所有用户添加头像特征值
     * @return
     */
    @Override
    public List<User> getAllUser(){

        return  queryFactory.selectFrom(qUser).fetch();
    }

}
